package org.yuanqiframework.yuanqi.web.auth.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.yuanqiframework.yuanqi.web.auth.annotation.Auth;
import org.yuanqiframework.yuanqi.web.auth.exception.ForbiddenException;
import org.yuanqiframework.yuanqi.web.auth.exception.UnauthorizedException;
import org.yuanqiframework.yuanqi.web.auth.service.IAuth;
import org.yuanqiframework.yuanqi.web.auth.utils.AuthUtils;
import org.yuanqiframework.yuanqi.web.auth.utils.JoinPointUtils;
import org.yuanqiframework.yuanqi.web.utils.ServletUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Set;

@Slf4j
@Aspect
@ConditionalOnProperty(prefix = "yuanqi.web", name = "auth-with-annotation", havingValue = "true")
@Configuration
public class AuthAspect {
    @Resource
    private IAuth auth;

    @PostConstruct
    public void init() {
        log.info("enable auth with annotation");
    }

    @Pointcut("@within(org.yuanqiframework.yuanqi.web.auth.annotation.Auth) || @annotation(org.yuanqiframework.yuanqi.web.auth.annotation.Auth)")
    public void cut() {
    }

    @Before(value = "cut()")
    public void before(JoinPoint joinPoint) throws IOException {
        Auth methodAnnotation = (Auth) JoinPointUtils.getAnnotation(joinPoint, Auth.class);
        // 如果没有方法注解，尝试获取类上的注解
        if (methodAnnotation == null) {
            Class<?> targetClass = joinPoint.getTarget().getClass();
            methodAnnotation = targetClass.getAnnotation(Auth.class);
        }
        if (methodAnnotation == null) {
            return; // 理论上不会发生，因为切入点已经确保有注解
        }
        //开放接口权限
        boolean publicApi = methodAnnotation.publicApi();
        if (publicApi) {
            return;
        }
        String[] roles = methodAnnotation.roles();
        if (roles.length == 0) {
            Set<String> userRoles = auth.provideRoles(ServletUtils.getRequest());
            if (userRoles == null || userRoles.isEmpty()) {
                //未认证
                throw new UnauthorizedException();
            }
            return;
        }
        Set<String> userRoles = auth.provideRoles(ServletUtils.getRequest());
        if (userRoles == null || userRoles.isEmpty()) {
            //未认证
            throw new UnauthorizedException();
        }
        boolean passed = true;
        if (!AuthUtils.hasRole(methodAnnotation, userRoles)) {
            passed = false;
        }
        if (!passed) {
            //权限不足
            throw new ForbiddenException();
        }
    }
}
